﻿/**
 * MOTOR 2 - RAPID FLASH DYNAMICS
 * Copyright (c) 2007-2008, Michael Baczynski
 * Based on the Box2D Engine by Erin Catto, http://www.box2d.org
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * * Neither the name of the polygonal nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without specific
 *   prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package de.polygonal.motor2.collision.nbody
{
	import de.polygonal.ds.ArrayedQueue;
	import de.polygonal.motor2.Constants;
	import de.polygonal.motor2.World;
	import de.polygonal.motor2.collision.nbody.IPairCallback;
	import de.polygonal.motor2.collision.nbody.Pair;
	import de.polygonal.motor2.collision.shapes.ShapeSkeleton;
	import de.polygonal.motor2.math.AABB2;

	//TODO rename to AbstractBroadPhase
	public class BroadPhase implements IBroadPhase
	{
		protected var _xmin:Number, _xmax:Number;
		protected var _ymin:Number, _ymax:Number;
		
		public var world:World;
		
		public var worldAABB:AABB2;
		
		
		public var proxyList:ShapeProxy;
		public var proxyIdQueue:ArrayedQueue;
		
		public var proxyQue:ArrayedQueue;
		public var proxyLookup:Array;
		
		public var pairBufferCount:int;
		public var pairBuffer:Array;
		
		protected var _pairHandler:IPairCallback;
		
		//worldAABB:AABB2
		public function BroadPhase()
		{
			init();
		}
		
		public function setWorld(world:World):void
		{
			this.world = world;
		}
		
		public function setWorldBounds(aabb:AABB2):void
		{
			this.worldAABB = aabb;
			
			_xmin = worldAABB.xmin; _ymin = worldAABB.ymin;
			_xmax = worldAABB.xmax; _ymax = worldAABB.ymax;
		}
		
		public function setPairHandler(pairHandler:IPairCallback):void
		{
			_pairHandler = pairHandler;
		}
		
		public function inRange(aabb:AABB2):Boolean
		{
			return false;
		}
		
		public function insideBounds(xmin:Number, ymin:Number, xmax:Number, ymax:Number):Boolean
		{
			return false;
		}
		
		public function search():void
		{
		}
		
		public var freeProxy:int;
		
		public function moveProxy(proxyId:int):void
		{
		}
		
		//TODO replace typed shape with untyed udata or aabb?
		public function createProxy(groupIndex:int, categoryBits:int, maskBits:int, shape:ShapeSkeleton):int
		{
			/*
			//b2Settings.b2Assert(m_freeProxy != b2Pair.b2_nullProxy);
			//if (freeProxy:int == Pair.NULL_PROXY)
			//{
				//return Pair.NULL_PROXY
			//}
			
			//flush pair buffer
			/*flush();
			
			var proxyId:uint = freeProxy;
			proxy = proxyPool[proxyId];
			freeProxy = proxy.GetNext();
			*/
			
			var proxy:ShapeProxy = proxyQue.dequeue();
			proxy.shape          = shape;
			proxy.groupIndex     = groupIndex;
			proxy.categoryBits   = categoryBits;
			proxy.maskBits       = maskBits;
			proxyLookup[proxy.id] = proxy;
			
			proxy.next = proxyList;
			if (proxyList) proxyList.prev = proxy;
			proxyList = proxy;
			
			return proxy.id;
		}
		
		public function removeProxy(proxyId:int):void
		{
			var proxy:ShapeProxy = proxyLookup[proxyId];
			if (proxy.prev) proxy.prev.next = proxy.next;
			if (proxy.next) proxy.next.prev = proxy.prev;
			if (proxy == proxyList) proxyList = proxy.next;
			
			proxy.init();
			proxyQue.enqueue(proxy);
		}
		
		public function Flush():void
		{
			/*
			var i:int;
			
			var removeCount:int = 0;
			
			var bufferedPair:BufferedPair;
			var pair:Pair;
			
			for (i = 0; i < pairBufferCount; i++)
			{
				bufferedPair = pairBuffer[i];
				
				//pair = m_pairManager.Find(m_pairBuffer[i].proxyId1, m_pairBuffer[i].proxyId2);
				
				var proxy1:ShapeProxy = proxyPool[pair.proxyId1];
				var proxy2:ShapeProxy = proxyPool[pair.proxyId2];
				
				if (pair.isRemoved())
				{
					//b2Settings.b2Assert(TestOverlap(proxy1, proxy2) == false);
					
					if (pair.isReceived())
					{
						//m_pairCallback.PairRemoved(proxy1.userData, proxy2.userData, pair.userData);
					}
					
					// Store the ids so we can actually remove the pair below.
					bufferedPair = pairBuffer[removeCount++];
					bufferedPair.proxyId1 = pair.proxyId1;
					bufferedPair.proxyId2 = pair.proxyId2;y
				}
				else
				{
					//b2Settings.b2Assert(TestOverlap(proxy1, proxy2) == true);
					pair.clearBuffered();
					if (pair.isReceived() == false)
					{
						//pair.userData = m_pairCallback.PairAdded(proxy1.userData, proxy2.userData);
						pair.setReceived();
					}
				}
			}
			
			for (i = 0; i < removeCount; ++i)
			{
				bufferedPair = pairBuffer[i];
				m_pairManager.Remove(bufferedPair.proxyId1, bufferedPair.proxyId2);
			}
			
			pairBufferCount = 0;
			*/
		}
		
		public function init():void
		{
			var sp:ShapeProxy;
			var k:int = Constants.k_maxProxies;
			var i:int;
			
			proxyLookup = new Array(k);
			proxyQue = new ArrayedQueue(k);
			for (i = 0; i < k; i++)
			{
				sp = new ShapeProxy();
				sp.id = i + 1;
				proxyQue.enqueue(sp);
				proxyLookup[sp.id] = sp;
			}
			
			proxyList = null;
			//_pairs = new Dictionary(true);
			
			pairBuffer = new Array(Constants.k_maxPairs);
			pairBufferCount = 0;
			
			/* fill pair buffer */
			k = Constants.k_maxPairs;
//			for (i = 0; i < k; i++) pairBuffer[i] = new BufferedPair();
		}
		
		protected function addBufferedPair(id1:int, id2:int):void
		{
			var p1:ShapeProxy = proxyLookup[id1];
			var p2:ShapeProxy = proxyLookup[id2];
			
			if (p1.groupIndex == p2.groupIndex)
				if (p1.groupIndex != 0)
					if (p1.groupIndex <= 0)
						return;
			
			if ((p1.maskBits & p2.categoryBits) == 0) return;
			if ((p1.categoryBits & p2.maskBits) == 0) return;
			
			//var pair:Pair = m_pairManager.Add(id1, id2);
			var pair:Pair;
			
			if (pair == null)
			{
				return;
			}
			
			//if this pair is not in the pair buffer ...
			if (pair.isBuffered() == false)
			{
				// This must be a new pair.
				//b2Settings.b2Assert(pair.IsReceived() == false);
				
				// If there is room in the pair buffer ...
				if (pairBufferCount < Constants.k_maxPairs)
				{
					// Add it to the pair buffer.
					pair.setBuffered();
					
//					var bp:BufferedPair = pairBuffer[pairBufferCount];
//					bp.proxyId1 = pair.proxyId1;
//					bp.proxyId2 = pair.proxyId2;
					pairBufferCount++;
				}
				
				//b2Settings.b2Assert(pairBufferCount <= m_pairManager.GetCount());
			}
			// Confirm this pair for the subsequent call to Flush.
			pair.clearRemoved();
		}
		
		protected function removeBufferedPair(id1:int, id2:int):void
		{
			//b2Settings.b2Assert(m_proxyPool[id1].IsValid() && m_proxyPool[id2].IsValid());
			
			//var pair:Pair = m_pairManager.Find(id1, id2);
			var pair:Pair;
			
			if (pair == null)
			{
				return;
			}
			
			// If this pair is not in the pair buffer ...
			if (pair.isBuffered() == false)
			{
				// This must be an old pair.
				//b2Settings.b2Assert(pair.IsReceived());
				
				if (pairBufferCount < Constants.k_maxPairs)
				{
					pair.setBuffered();
					pairBuffer[pairBufferCount].proxyId1 = pair.proxyId1;
					pairBuffer[pairBufferCount].proxyId2 = pair.proxyId2;
					++pairBufferCount;
				}
				
				//b2Settings.b2Assert(pairBufferCount <= m_pairManager.GetCount());
			}
			
			pair.setRemoved();
		}
	}
}